using System;
using System.Globalization;
using System.IO;
using System.Text;
using log4net;
using log4net.Core;
using NVelocity;
using NVelocity.App;
using NVelocity.Runtime;
using Quiki.Templates;

namespace Quiki.DocumentGraphProcessors
{
    public class HtmlDocumentsGenerator : IDocumentGraphProcessor
    {
        public HtmlDocumentsGenerator(
            DocumentProcessorSettings settings,
            TemplatesRegistry templatesRegistry,
            FileSet documentFiles,
            ILogger logger)
        {
            this.settings = settings;
            this.templatesRegistry = templatesRegistry;
            this.documentFiles = documentFiles;
            this.logger = logger;
        }

        public void Execute(DocumentGraph documentGraph)
        {
            LoadTemplate();

            foreach (WikiDocument documentInfo in documentGraph.Documents.Values)
                GenerateHtmlDocument(documentInfo, documentGraph);
        }

        private void LoadTemplate()
        {
            if (String.IsNullOrEmpty(settings.TemplateFileName))
                throw new ArgumentException("Template file name was not specified");

            if (false == File.Exists(settings.TemplateFileName))
            {
                string message = string.Format(
                    CultureInfo.InvariantCulture,
                    "Template file '{0}' does not exist",
                    settings.TemplateFileName);
                throw new ArgumentException(message);
            }

            string directoryName = Path.GetDirectoryName(settings.TemplateFileName);
            if (String.IsNullOrEmpty(directoryName))
                directoryName = ".";
            string templateDir = Path.GetFullPath(directoryName);

            VelocityEngine velocity = new VelocityEngine();
            velocity.SetProperty(RuntimeConstants.RESOURCE_LOADER, "file");
            velocity.SetProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, templateDir);
            velocity.Init();

            template = velocity.GetTemplate(
                Path.GetFileName(settings.TemplateFileName),
                new UTF8Encoding(false).WebName);
        }

        private void GenerateHtmlDocument(WikiDocument document, DocumentGraph documentGraph)
        {
            logger.PushContext(document.Reference.ToString());

            HtmlFormatter formatter = new HtmlFormatter(templatesRegistry);
            formatter.Document = document;
            formatter.DocumentGraph = documentGraph;
            string html = formatter.Format(document.RootElement);

            string outputFileName = document.ConstructFileName(null, ".html");
            string fullOutputFileName = Path.Combine(settings.OutputDir, outputFileName);

            CreateDirectoryIfNecessary(fullOutputFileName);

            document.Body = html;
            GenerateHtmlFile (document, fullOutputFileName);
            documentFiles.RegisterFile(outputFileName);

            logger.PopContext();
        }

        private void GenerateHtmlFile(WikiDocument document, string outputFileName)
        {
            VelocityContext velocityContext = new VelocityContext();
            velocityContext.Put("quiki", new QuikiInfo());
            velocityContext.Put("doc", document);
            velocityContext.Put("props", settings.CustomProperties);

            using (Stream stream = File.Open(outputFileName, FileMode.Create))
            {
                using (TextWriter writer = new StreamWriter(stream, new UTF8Encoding(false)))
                {
                    template.Merge(velocityContext, writer);
                }
            }

            logger.Log(Level.Info, "Generated HTML file '{0}'", outputFileName);
        }

        private static void CreateDirectoryIfNecessary(string outputFileName)
        {
            string outputDirName = Path.GetDirectoryName(outputFileName);
            if (false == Directory.Exists(outputDirName))
            {
                log.DebugFormat("Creating directory '{0}'", outputDirName);
                Directory.CreateDirectory(outputDirName);
            }
        }

        private static readonly ILog log = LogManager.GetLogger(typeof(HtmlDocumentsGenerator));
        private readonly DocumentProcessorSettings settings;
        private readonly TemplatesRegistry templatesRegistry;
        private readonly FileSet documentFiles;
        private readonly ILogger logger;
        private Template template;
    }
}